package com.googlewell.zelix.tool;

import me.coley.recaf.parse.bytecode.Disassembler;
import me.coley.recaf.search.Context;
import me.coley.recaf.util.OpcodeUtil;
import me.coley.recaf.util.RegexUtil;
import me.coley.recaf.util.StringUtil;
import org.objectweb.asm.Handle;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.ArrayList;
import java.util.List;

public class ClassTool {

    /**
     * 通过文本指令获取相应指令node
     * me.coley.recaf.parse.bytecode.Parse类含有大多数解析,这个取自MethodInsnParser解析流程
     *
     * @param insn 合规的指令
     * @return 指令有问题返回null
     */
    public static MethodInsnNode getMethodInsnNodeByInsnString(String insn) {
        String[] trim = insn.trim().split("\\s+");
        // owner & name & desc
        String data = trim[1];
        int dot = data.indexOf('.');
        if (dot == -1) {
            return null;
        }
        int parenthesis = data.indexOf('(');
        if (parenthesis < dot) {
            return null;
        }
        String typeS = data.substring(0, dot);
        String nameS = data.substring(dot + 1, parenthesis);
        String descS = data.substring(parenthesis);

        return new MethodInsnNode(OpcodeUtil.nameToOpcode(trim[0]), typeS, nameS, descS, false);
    }

    /**
     * 通过文本指令获取相应指令node
     * me.coley.recaf.parse.bytecode.Parse类含有大多数解析,这个取自FieldInsnParser解析流程
     *
     * @param insn
     * @return 指令有问题返回null
     */
    public static FieldInsnNode getFieldInsnNodeByInsnString(String insn) {
        String[] trim = insn.trim().split("\\s+");
        if (trim.length < 3) {
            return null;
        }
        // owner & name
        String typeAndName = trim[1];
        int dot = typeAndName.indexOf('.');
        if (dot == -1) {
            return null;
        }
        String typeS = typeAndName.substring(0, dot);
        String nameS = typeAndName.substring(dot + 1);
        return new FieldInsnNode(OpcodeUtil.nameToOpcode(trim[0]), typeS, nameS, trim[2]);
    }

    /**
     * 从所有指令中查找某条指令的索引
     *
     * @param codeLines
     * @param ins
     * @return 未找到返回-1
     */
    public static int getCodeLine(String[] codeLines, String ins) {
        for (int i = 0; i < codeLines.length; i++) {
            if (codeLines[i].equals(ins)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取方法节点的汇编指令
     *
     * @param methodNode
     * @return
     */
    public static String[] getMethodCodeLines(MethodNode methodNode) {
        //得到汇编代码
        Disassembler disassembler = new Disassembler();
        String code = disassembler.disassemble(methodNode);
        //得到指令数组
        String[] codeLines = StringUtil.splitNewline(code);
        return codeLines;
    }

    /**
     * 查找InvokeDynamic指令
     *
     * @param classname 类
     * @param codeLines 指令
     * @param self      是否是本类处理
     * @return 未查找到返回null
     */
    public static InvokeDynamicInsnNode searchInvokeDynamic(String classname, String[] codeLines, boolean self) {
        if (codeLines == null) {
            return null;
        }
        for (int i = 0; i < codeLines.length; i++) {
            if (codeLines[i].startsWith("INVOKEDYNAMIC")) {
                InvokeDynamicInsnNode node = getInvokeDynamicInsnNodeByInsnString(codeLines[i]);
                if (self && node.bsm.getOwner().equals(classname)) {//本类内处理多为字符串解密
                    return node;
                }
                if (!self && !node.bsm.getOwner().equals(classname)) {//外部类为成员属性和方法调用
                    return node;
                }
            }
        }
        return null;
    }

    /**
     * 得到类的Context,查询结果最上层为类
     *
     * @param ctx
     * @return
     */
    public static Context.ClassContext getClassContext(Context<?> ctx) {
        while (!ctx.getClass().equals(Context.ClassContext.class))
            ctx = ctx.getParent();
        return (Context.ClassContext) ctx;
    }


    private final static String BRACKET_WRAPPING = "\\w*\\[.+]";
    private final static String BRACKET_WRAPPING_OR_EMPTY = "\\w*\\[.*]";

    /**
     * 通过文本指令获取相应指令node
     * me.coley.recaf.parse.bytecode.Parse类含有大多数解析,这个取自InvokeDynamicParser解析流程
     *
     * @param line
     * @return
     */
    public static InvokeDynamicInsnNode getInvokeDynamicInsnNodeByInsnString(String line) {
        // Split here:
        //              v    v    v
        // INVOKEDYNAMIC name desc handle[...] args[...]
        String[] trim = line.trim().split("\\s+(?=.*\\[(?=.*\\[))");
        if (trim.length < 4) {
            return null;
        }
        // 0 = op
        // 1 = name
        // 2 = desc
        // op trim[0]
        // name trim[1]
        // desc trim[2]
        String name = trim[1];
        String desc = trim[2];

        // handle & args
        // - Split space between handle and args
        trim = line.substring(RegexUtil.indexOf("(?:(?<=\\s)handle|handle|\\s)\\[\\s*H_", line)).split("(?<=\\])\\s+(?=.*\\[)");

        // handle
        String handleS = trim[0];
        if (!handleS.matches(BRACKET_WRAPPING)) {
            return null;
        }
        handleS = handleS.substring(handleS.indexOf('[') + 1, handleS.indexOf(']'));

        // args
        String argsS = trim[1];
        if (!argsS.matches(BRACKET_WRAPPING_OR_EMPTY)) {
            return null;
        }
        argsS = argsS.substring(argsS.indexOf('[') + 1, argsS.lastIndexOf(']'));
        // if the args has a string with commas, this will break...
        // we'll fix that whenever it happens
        List<Object> args = new ArrayList<>();
        if (!argsS.isEmpty()) {
            String[] argsSplit = argsS.split(",\\s*(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)");
            for (String arg : argsSplit) {
                args.add(parseArg(arg));
            }
        }
        return new InvokeDynamicInsnNode(name, desc, getHandleByInsnString(handleS), args);
    }

    /**
     * 还未碰到in有args的情况,
     * me.coley.recaf.parse.bytecode.Parse类含有大多数解析,这个取自InvokeDynamicParser解析流程
     *
     * @param arg
     * @return
     */
    public static Object parseArg(String arg) {
        if (arg.contains("\"")) {
            //String
        } else if (arg.matches("\\d+")) {
            //Int
        } else if (arg.matches("\\d+[LlJj]?")) {
            //Long
        } else if (arg.matches("\\d+\\.\\d+[Ff]?")) {
            //Float
        } else if (arg.matches("\\d+\\.\\d+[Dd]?")) {
            //Double
        } else if (arg.matches(BRACKET_WRAPPING)) {
            //Handle
            arg = arg.substring(arg.indexOf('[') + 1, arg.indexOf(']'));
        } else {
            //Desc描述
        }
        return null;
    }

    /**
     * 通过文本指令获取Handle
     * me.coley.recaf.parse.bytecode.Parse类含有大多数解析,这个取自HandleParser解析流程
     *
     * @param insn
     * @return 指令有问题返回null
     */
    public static Handle getHandleByInsnString(String insn) {
        String[] trim = insn.trim().split("\\s+");
        if (trim.length < 2) {
            return null;
        }
        // op   trim[0]
        // owner & name & desc
        String data = trim[1];
        int dot = data.indexOf('.');
        if (dot == -1) {
            return null;
        }
        // Determine split index, for field or method type
        int descSplit = data.indexOf('(');
        if (descSplit < dot) {
            descSplit = data.indexOf(' ');
            if (descSplit < dot) {
                return null;
            }
        }
        // owner
        String typeS = data.substring(0, dot);
        // name
        String nameS = data.substring(dot + 1, descSplit);
        // desc
        String descS = data.substring(descSplit);
        return new Handle(OpcodeUtil.nameToTag(trim[0]), typeS, nameS, descS, false);
    }
}
